
/*========================================================================================
 *                                                                                      *
 *               Copyright(c) 2013-2014, Cha SangWoo, All rights reserved.              *
 *                                                                                      *
 * This file is part of ChaOS.                                                          *
 *                                                                                      *
 * Permission to use, copy, modify, and distribute this software in source and binary   *
 * forms and its documentation for any purpose and without fee is hereby granted,       *
 * provided that the above copyright notice appear in all copies and that both that     *
 * copyright notice and this permission notice appear in supporting documentation.      *
 *                                                                                      *
 * THIS SOFTWARE IS PROVIDED BY THE CHA SANGWOO AND CONTRIBUTORS "AS IS" AND ANY        *
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED            *
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.   *
 * IN NO EVENT SHALL CHA SANGWOO OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,    *
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT         *
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,          *
 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF            *
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE      *
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,                    *
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.                                   *
 *                                                                                      *
========================================================================================*/

#include "co_core.h"
#include "co_ring_buffer.h"
#include "co_semaphore.h"

#if (FLAG_USE_CHAOS_RING_BUFFER == CO_ENABLE)

#ifdef __cplusplus
    namespace ChaOS {
#endif

//////////////////////////////////////  < BEGIN >  ///////////////////////////////////////

extern VOID _co_queue_memcpy(UINT8 *pdst, UINT8 *psrc, UINT size);

/*======================================================================================*/
/*!
    @fn             OBJECT co_ring_buffer_create(UINT count)

    @brief          Create a Ring Buffer

    @return         Object for Ring Buffer
*/
/*======================================================================================*/
OBJECT co_ring_buffer_create(UINT count)
{
    UINT                size;
    RING_BUFFER_CB     *pring_buffer;

    CO_ASSERT(count > 0);

    size  = sizeof(RING_BUFFER_CB) - sizeof(UINT32);
    size += count;

    // allocate heap memory
    pring_buffer = (RING_BUFFER_CB*) co_heap_alloc(size);
    CO_ASSERT(pring_buffer);

    // create semaphore for getting
    pring_buffer->objGetCount = co_semaphore_create(0, count);
    CO_ASSERT(pring_buffer->objGetCount);

    // create semaphore for putting
    pring_buffer->objPutCount = co_semaphore_create(count, count);
    CO_ASSERT(pring_buffer->objPutCount);

    // set object ID
    OBJID_SET(pring_buffer, OBJ_RING_BUFFER_ID);

    pring_buffer->read_pos  = 0;
    pring_buffer->write_pos = 0;
    pring_buffer->max_count = count;

    return (OBJECT) pring_buffer;
}


/*======================================================================================*/
/*!
    @fn             VOID co_ring_buffer_put(OBJECT object, UINT8 data)

    @brief          Enter critical area (get a mutex)

    @param [in]     object - Object for Ring Buffer
    @param [in]     data - 

    @return         
*/
/*======================================================================================*/
UINT co_ring_buffer_put(OBJECT object, UINT8 *pdata, UINT16 size)
{
    RING_BUFFER_CB     *pring_buffer = (RING_BUFFER_CB*) object;
    UINT16              count, written = 0;

    // check object ID
    OBJID_CHECK(pring_buffer, OBJ_RING_BUFFER_ID);

    if (size == 0)
        return 0;

    // check that buffer is full.
    if (co_semaphore_take(pring_buffer->objPutCount, 0) == CO_OK_SUCCESS)
    {
        ///////////////////
        CO_ENTER_CRITICAL();

        count = co_semaphore_get_cur_count(pring_buffer->objPutCount) + 1;

        size = CO_MIN(size, count);

        while (written < size)
        {
            // get count to copy
            if (pring_buffer->write_pos >= pring_buffer->read_pos)
                count = pring_buffer->max_count - pring_buffer->write_pos;
            else
                count = pring_buffer->read_pos - pring_buffer->write_pos;

            count = CO_MIN(count, (size - written));
            if (count == 0)
                break;

            // copy data
            _co_queue_memcpy(&(pring_buffer->data[pring_buffer->write_pos]), pdata, count);

            pring_buffer->write_pos += count;
            pdata += count;
            written += count;

            if (pring_buffer->write_pos >= pring_buffer->max_count)
                pring_buffer->write_pos = 0;
        }

        if (written > 1)
            co_semaphore_sub_count(pring_buffer->objPutCount, written-1);

        if (written > 0)
            co_semaphore_add_count(pring_buffer->objGetCount, written);

        CO_LEAVE_CRITICAL();
        ////////////////////
    }

    return written;
}


/*======================================================================================*/
/*!
    @fn             UINT co_ring_buffer_get(OBJECT object, UINT8 *pdata)

    @brief          

    @param [in]     object  - Object for Ring Buffer
    @param [in]     pdata - 

    @return         
*/
/*======================================================================================*/
UINT co_ring_buffer_get(OBJECT object, UINT8 *pdata, UINT16 size, UINT16 timeout)
{
    RING_BUFFER_CB     *pring_buffer = (RING_BUFFER_CB*) object;
    UINT16              count, read = 0;

    // check object ID
    OBJID_CHECK(pring_buffer, OBJ_RING_BUFFER_ID);

    if (size == 0)
        return 0;

    // check that buffer is empty
    if (co_semaphore_take(pring_buffer->objGetCount, timeout) == CO_OK_SUCCESS)
    {
        ///////////////////
        CO_ENTER_CRITICAL();

        count = co_semaphore_get_cur_count(pring_buffer->objGetCount) + 1;

        size = CO_MIN(count, size);

        while (read < size)
        {
            // get count to copy
            if (pring_buffer->read_pos < pring_buffer->write_pos)
                count = pring_buffer->write_pos - pring_buffer->read_pos;
            else
                count = pring_buffer->max_count - pring_buffer->read_pos;

            count = CO_MIN(count, (size - read));
            if (count == 0)
                break;

            // copy data
            _co_queue_memcpy(pdata, &(pring_buffer->data[pring_buffer->read_pos]), count);

            pring_buffer->read_pos += count;
            pdata += count;
            read += count;

            if (pring_buffer->read_pos >= pring_buffer->max_count)
                pring_buffer->read_pos = 0;
        }

        if (read > 1)
            co_semaphore_sub_count(pring_buffer->objGetCount, read-1);

        if (read > 0)
            co_semaphore_add_count(pring_buffer->objPutCount, read);

        CO_LEAVE_CRITICAL();
        ////////////////////
    }

    return read;
}

/*======================================================================================*/
/*!
    @fn             UINT co_ring_buffer_get_count(OBJECT object)

    @brief          

    @param [in]     object  - Object for Ring Buffer

    @return         Number of  the Ring Buffer's data.
*/
/*======================================================================================*/
UINT co_ring_buffer_get_count(OBJECT object)
{
    RING_BUFFER_CB     *pring_buffer = (RING_BUFFER_CB*) object;

    // check object ID
    OBJID_CHECK(pring_buffer, OBJ_RING_BUFFER_ID);

    return co_semaphore_get_cur_count(pring_buffer->objGetCount);
}

//////////////////////////////////////  <  END  >  ///////////////////////////////////////

#ifdef __cplusplus
    } /* namespace ChaOS */
#endif

#endif // (FLAG_USE_CHAOS_RING_BUFFER == CO_ENABLE)

